package com.kymatrix.mpo.plus.bpm.server.controller.task;

import cn.hutool.core.collection.CollUtil;
import com.kymatrix.mpo.plus.system.api.service.UserApi;
import com.kymatrix.mpo.plus.system.api.dto.UserDto;
import com.kymatrix.mpo.plus.system.api.service.DeptApi;
import com.kymatrix.mpo.plus.system.api.dto.DeptmentDto;
import com.kymatrix.mpo.plus.bpm.server.convert.task.BpmTaskConvert;
import com.kymatrix.mpo.plus.bpm.server.model.entity.definition.BpmFormEntity;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.ApproveBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.CreateSignBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.DelegateBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.DeleteSignBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.PageQueryBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.QueryBpmTaskResp;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.RejectBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.ReturnBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.TransferBpmTaskReq;
import com.kymatrix.mpo.plus.bpm.server.service.BpmFormService;
import com.kymatrix.mpo.plus.bpm.server.service.BpmProcessInstanceService;
import com.kymatrix.mpo.plus.bpm.server.service.BpmTaskService;
import com.kymatrix.mpo.plus.common.business.common.PageResp;
import com.kymatrix.mpo.plus.common.business.common.ResultResp;
import com.kymatrix.mpo.plus.common.springsecurity.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertList;
import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertSet;
import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertSetByFlatMap;

@Api(tags = "BPM 流程任务实例相关请求处理类")
@RestController
@Validated
public class BpmTaskController {

    @Resource
    private BpmTaskService taskService;
    @Resource
    private BpmProcessInstanceService processInstanceService;
    @Resource
    private BpmFormService formService;

    @Resource
    private UserApi userApi;
    @Resource
    private DeptApi deptApi;

    @GetMapping("/bpm/task/todo-page")
    @ApiOperation(value = "获取 Todo 待办任务分页")
    public ResultResp<PageResp<QueryBpmTaskResp>> getTaskTodoPage(@Valid PageQueryBpmTaskReq req) {
        PageResp<Task> pageResult = taskService.getTaskTodoPage(SecurityUtils.getLoginUserId(), req);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return ResultResp.ok(new PageResp<>());
        }

        // 拼接数据
        Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
                convertSet(pageResult.getList(), Task::getProcessInstanceId));
        Map<String, UserDto> userMap = userApi.getUserMap(
                convertSet(processInstanceMap.values(), ProcessInstance::getStartUserId));
        return ResultResp.ok(BpmTaskConvert.buildTodoTaskPage(pageResult, processInstanceMap, userMap));
    }

    @GetMapping("/bpm/task/done-page")
    @ApiOperation(value = "获取 Done 已办任务分页")
    public ResultResp<PageResp<QueryBpmTaskResp>> getTaskDonePage(@Valid PageQueryBpmTaskReq req) {
        PageResp<HistoricTaskInstance> pageResult = taskService.getTaskDonePage(SecurityUtils.getLoginUserId(), req);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return ResultResp.ok(new PageResp<>());
        }

        // 拼接数据
        Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));
        Map<String, UserDto> userMap = userApi.getUserMap(
                convertSet(processInstanceMap.values(), HistoricProcessInstance::getStartUserId));
        return ResultResp.ok(BpmTaskConvert.buildTaskPage(pageResult, processInstanceMap, userMap, null));
    }

    @GetMapping("/bpm/task/manager-page")
    @ApiOperation(value = "获取全部任务的分页", notes = "用于【流程任务】菜单")
    public ResultResp<PageResp<QueryBpmTaskResp>> getTaskManagerPage(@Valid PageQueryBpmTaskReq req) {
        PageResp<HistoricTaskInstance> pageResult = taskService.getTaskPage(SecurityUtils.getLoginUserId(), req);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return ResultResp.ok(new PageResp<>());
        }

        // 拼接数据
        Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
                convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));
        // 获得 User 和 Dept Map
        Set<String> userIds = convertSet(processInstanceMap.values(), HistoricProcessInstance::getStartUserId);
        userIds.addAll(convertSet(pageResult.getList(), TaskInfo::getAssignee));
        Map<String, UserDto> userMap = userApi.getUserMap(userIds);
        Map<String, DeptmentDto> deptMap = deptApi.getDeptMap(
                convertSet(userMap.values(), UserDto::getDeptId));
        return ResultResp.ok(BpmTaskConvert.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap));
    }

    @GetMapping("/bpm/task/list-by-process-instance-id")
    @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的")
    public ResultResp<List<QueryBpmTaskResp>> getTaskListByProcessInstanceId(
            @RequestParam("processInstanceId") String processInstanceId) {
        List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId);
        if (CollUtil.isEmpty(taskList)) {
            return ResultResp.ok(Collections.emptyList());
        }

        // 拼接数据
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        // 获得 User 和 Dept Map
        Set<String> userIds = convertSetByFlatMap(taskList, task ->
                Stream.of(task.getAssignee(), task.getOwner()));
        userIds.add(processInstance.getStartUserId());
        Map<String, UserDto> userMap = userApi.getUserMap(userIds);
        Map<String, DeptmentDto> deptMap = deptApi.getDeptMap(
                convertSet(userMap.values(), UserDto::getDeptId));
        // 获得 Form Map
        Map<String, BpmFormEntity> formMap = formService.getFormMap(
                convertSet(taskList, task -> task.getFormKey()));
        return ResultResp.ok(BpmTaskConvert.buildTaskListByProcessInstanceId(taskList, processInstance,
                formMap, userMap, deptMap));
    }

    @PutMapping("/bpm/task/approve")
    @ApiOperation(value = "通过任务")
    public ResultResp<Boolean> approveTask(@Valid @RequestBody ApproveBpmTaskReq req) {
        taskService.approveTask(SecurityUtils.getLoginUserId(), req);
        return ResultResp.ok(true);
    }

    @PutMapping("/bpm/task/reject")
    @ApiOperation(value = "不通过任务")
    public ResultResp<Boolean> rejectTask(@Valid @RequestBody RejectBpmTaskReq req) {
        taskService.rejectTask(SecurityUtils.getLoginUserId(), req);
        return ResultResp.ok(true);
    }

    @GetMapping("/bpm/task/list-by-return")
    @ApiOperation(value = "获取所有可回退的节点", notes = "用于【流程详情】的【回退】按钮")
    public ResultResp<List<QueryBpmTaskResp>> getTaskListByReturn(@RequestParam("id") String id) {
        List<UserTask> userTaskList = taskService.getUserTaskListByReturn(id);
        return ResultResp.ok(convertList(userTaskList, userTask -> // 只返回 id 和 name
                new QueryBpmTaskResp().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId())));
    }

    @PutMapping("/bpm/task/return")
    @ApiOperation(value = "回退任务", notes = "用于【流程详情】的【回退】按钮")
    public ResultResp<Boolean> returnTask(@Valid @RequestBody ReturnBpmTaskReq reqVO) {
        taskService.returnTask(SecurityUtils.getLoginUserId(), reqVO);
        return ResultResp.ok(true);
    }

    @PutMapping("/bpm/task/delegate")
    @ApiOperation(value = "委派任务", notes = "用于【流程详情】的【委派】按钮")
    public ResultResp<Boolean> delegateTask(@Valid @RequestBody DelegateBpmTaskReq reqVO) {
        taskService.delegateTask(SecurityUtils.getLoginUserId(), reqVO);
        return ResultResp.ok(true);
    }

    @PutMapping("/bpm/task/transfer")
    @ApiOperation(value = "转派任务", notes = "用于【流程详情】的【转派】按钮")
    public ResultResp<Boolean> transferTask(@Valid @RequestBody TransferBpmTaskReq reqVO) {
        taskService.transferTask(SecurityUtils.getLoginUserId(), reqVO);
        return ResultResp.ok(true);
    }

    @PutMapping("/bpm/task/create-sign")
    @ApiOperation(value = "加签", notes = "before 前加签，after 后加签")
    public ResultResp<Boolean> createSignTask(@Valid @RequestBody CreateSignBpmTaskReq reqVO) {
        taskService.createSignTask(SecurityUtils.getLoginUserId(), reqVO);
        return ResultResp.ok(true);
    }

    @DeleteMapping("/bpm/task/delete-sign")
    @ApiOperation(value = "减签")
    public ResultResp<Boolean> deleteSignTask(@Valid @RequestBody DeleteSignBpmTaskReq reqVO) {
        taskService.deleteSignTask(SecurityUtils.getLoginUserId(), reqVO);
        return ResultResp.ok(true);
    }

    @GetMapping("/bpm/task/list-by-parent-task-id")
    @ApiOperation(value = "获得指定父级任务的子任务列表") // 目前用于，减签的时候，获得子任务列表
    public ResultResp<List<QueryBpmTaskResp>> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId) {
        List<Task> taskList = taskService.getTaskListByParentTaskId(parentTaskId);
        if (CollUtil.isEmpty(taskList)) {
            return ResultResp.ok(Collections.emptyList());
        }
        // 拼接数据
        Map<String, UserDto> userMap = userApi.getUserMap(convertSetByFlatMap(taskList,
                user -> Stream.of(user.getAssignee(), user.getOwner())));
        Map<String, DeptmentDto> deptMap = deptApi.getDeptMap(
                convertSet(userMap.values(), UserDto::getDeptId));
        return ResultResp.ok(BpmTaskConvert.buildTaskListByParentTaskId(taskList, userMap, deptMap));
    }

}
